package com.mikufufu.blog.utils;

import com.mikufufu.blog.common.constant.RegexStr;
import org.jetbrains.annotations.NotNull;
import org.springframework.lang.Nullable;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 字符串工具类
 * 
 */
public class StringUtils {

    private StringUtils(){}

    /**
     * 判断给定对象是否为空。
     * @param object 待检查的对象，可以是null，或者以下类型之一：String、Collection、Map、CharSequence、数组。
     * @return 如果对象为null，或者为空字符串（String）、空集合（Collection）、空映射（Map）、空字符序列（CharSequence）、空数组，返回true；否则返回false。
     */
    public static boolean isEmpty(@Nullable Object object) {
        if (object == null) {
            // 对象为null时，认为为空
            return true;
        } else if (object instanceof String) {
            // 对象为String类型时，使用String的isEmpty()方法判断
            return ((String) object).isEmpty();
        } else if (object instanceof Collection) {
            // 对象为Collection类型时，使用Collection的isEmpty()方法判断
            return ((Collection<?>) object).isEmpty();
        } else if (object instanceof Map) {
            // 对象为Map类型时，使用Map的isEmpty()方法判断
            return ((Map<?, ?>) object).isEmpty();
        } else if (object instanceof CharSequence) {
            // 对象为CharSequence类型时，判断其长度是否为0
            return ((CharSequence) object).length() == 0;
        } else if (object.getClass().isArray()){
            // 对象为数组时，判断数组长度是否为0
            return ((Object[])object).length == 0;
        } else {
            // 对于其他类型，均不认为为空
            return false;
        }
    }

    /**
     * 判断实体类是否不为空
     * @param object 实体类
     * @return true=实体类不为空；false=实体类为空
     */
    public static boolean isNotEmpty(@Nullable Object object) {
        return !isEmpty(object);
    }

    /**
     * 判断字符串是否为空白
     * @param str 字符串
     * @return true=字符串为空白；false=字符串不为空白
     */
    public static boolean isBlank(String str) {
        // 此处的str.trim().isEmpty()的写法等同于str.trim().length() == 0
        return str == null || str.trim().isEmpty();
    }

    /**
     * 判断字符串是否不为空白
     * @param str 字符串
     * @return true=字符串不为空白；false=字符串为空白
     */
    public static boolean isNotBlank(String str) {
        return !isBlank(str);
    }

    /**
     * 将list列表转换成字符串，以逗号分隔
     * @param list 列表
     * @return 字符串
     */
    public static String join(List<?> list) {
        return join(list, ",");
    }

    /**
     * 将list列表转换成字符串
     * @param list 列表
     * @param  separator 分隔符
     * @return 字符串
     */
    public static String join(List<?> list, String separator) {
        return join(list, separator, null,null);
    }

    /**
     * 将list列表转换成字符串
     * @param list 列表
     * @param  separator 分隔符
     * @param  prefix 前缀
     * @param  suffix 后缀
     * @return 字符串
     */
    public static String join(List<?> list, String separator, String prefix, String suffix) {
        StringBuilder sb = new StringBuilder();
        if (isNotEmpty(prefix)) {
            sb.append(prefix);
        }
        if (isNotEmpty(list)) {
            for (Object obj : list) {
                sb.append(obj);
                sb.append(separator);
            }
            // 删除最后一个分隔符
            sb.delete(sb.length() - separator.length(), sb.length());
        }
        if (isNotEmpty(suffix)) {
            sb.append(suffix);
        }
        return sb.toString();
    }



    /**
     * 将字符串形式的列表转换成字符串列表。
     * @param listStr 表示列表的字符串，各个元素通过","分隔。
     * @return 转换后的字符串列表。
     */
    public static List<String> toStrList(String listStr) {
        return toStrList(listStr,",");
    }

    /**
     * 将字符串形式的列表转换成字符串列表。
     * @param listStr 表示列表的字符串，各个元素通过separator分隔。
     * @param separator 用于分割字符串列表元素的字符。
     * @return 转换后的字符串列表。
     */
    public static List<String> toStrList(String listStr, String separator) {
        return toList(listStr, separator,String.class);
    }

    /**
     * 将字符串形式的列表转换成指定类型的实体列表。
     * @param listStr 表示列表的字符串，各个元素通过separator分隔。
     * @param clazz 要转换成的实体类型。
     * @return 转换后的实体列表。
     */
    public static <T> List<T> toList(String listStr, Class<T> clazz) {
        return toList(listStr, ",",clazz);
    }

    /**
     * 将字符串形式的列表转换成指定类型的实体列表。
     * @param listStr 表示列表的字符串，各个元素通过separator分隔。
     * @param separator 用于分割字符串列表元素的字符。
     * @param clazz 要转换成的实体类型。
     * @return 转换后的实体列表。
     */
    public static <T> List<T> toList(String listStr, String separator,Class<T> clazz) {
        return toList(listStr, separator,null,null,clazz);
    }

    /**
     * 将字符串形式的列表转换成指定类型的实体列表。
     * @param listStr 表示列表的字符串，各个元素通过separator分隔。
     * @param separator 用于分割字符串列表元素的字符。
     * @param prefix 每个元素前可能存在的前缀。
     * @param suffix 每个元素后可能存在的后缀。
     * @param clazz 要转换成的实体类型。
     * @return 转换后的实体列表。
     */
    public static <T> List<T> toList(String listStr,String separator, String prefix, String suffix ,Class<T> clazz) {
        List<T> list = new ArrayList<>();
        // 如果输入的字符串不为空，则进行分割和转换
        if (isNotEmpty(listStr)) {
            String[] strArray = listStr.split(separator);
            for (String str : strArray) {
                // 如果存在前缀，则移除
                if (isNotEmpty(prefix) && str.length() >= prefix.length()) {
                    str = str.substring(prefix.length());
                }
                // 如果存在后缀，则移除
                if (isNotEmpty(suffix) && str.length() >= suffix.length()) {
                    str = str.substring(0, str.length() - suffix.length());
                }
                // 将字符串转换成指定类型并添加到列表中
                //list.add((T)convertStringToType(clazz, str)); 下列语句解决强制类型转换泛型警告问题
                //list.add(clazz.cast(convertStringToType(clazz, str)));
                list.add(convertStringToType(clazz, str));
            }
        }
        return list;
    }

    /**
     * 将字符串转换为指定类型。只支持String、Integer、Long类型。
     * @param clazz 要转换成的类型。
     * @param str 要转换的字符串。
     * @return 转换后的对象。
     */
    public static <T> T convertStringToType(Class<T> clazz, String str) {
        if (clazz.isAssignableFrom(String.class)) {
            // 如果目标类型就是String，则直接返回
            return convert(str, clazz);
        } else if (clazz.equals(Integer.class) || clazz.equals(int.class)) {
            // 将String转换为Integer
            return convert(Integer.valueOf(str), clazz);
        } else if (clazz.equals(Long.class) || clazz.equals(long.class)) {
            // 将String转换为Long
            return convert(Long.valueOf(str), clazz);
        } else {
            throw new IllegalArgumentException("不支持的类型：" + clazz);
        }
    }

    /**
     * 将源对象转换为指定类型。
     *
     * @param source 源对象，需要转换的对象。
     * @param clazz 目标类型，指定要将源对象转换成的类型。
     * @return 转换后的对象。如果源对象不为null，则返回转换后的对象；如果源对象为null，则返回null。
     */
    public static <T> T convert(Object source, Class<T> clazz){
        if(source != null){
            // 源对象不为null时，尝试将其转换为指定类型并返回
            return clazz.cast(source);
        }else {
            // 源对象为null时，直接返回null
            return null;
        }
    }


    /**
     * 将字符串形式的列表转换成长整型列表。
     * @param listStr 表示列表的字符串，各个元素通过","分隔。
     * @return 转换后的字符串列表。
     */
    public static List<Long> toLongList(String listStr) {
        return toLongList(listStr,",");
    }

    /**
     * 将字符串形式的列表转换成长整型列表。
     * @param listStr 表示列表的字符串，各个元素通过separator分隔。
     * @param separator 用于分割字符串列表元素的字符。
     * @return 转换后的字符串列表。
     */
    public static List<Long> toLongList(String listStr, String separator) {
        return toList(listStr, separator,Long.class);
    }

    /**
     * 将字符串形式的列表转换成长整型列表。
     * @param listStr 表示列表的字符串，各个元素通过","分隔。
     * @return 转换后的字符串列表。
     */
    public static List<Integer> toIntegerList(String listStr) {
        return toIntegerList(listStr,",");
    }

    /**
     * 将字符串形式的列表转换成长整型列表。
     * @param listStr 表示列表的字符串，各个元素通过separator分隔。
     * @param separator 用于分割字符串列表元素的字符。
     * @return 转换后的字符串列表。
     */
    public static List<Integer> toIntegerList(String listStr, String separator) {
        return toList(listStr, separator,Integer.class);
    }

    /**
     * 将标识符替换成对应的值
     * @param destinationString 目标字符串
     * @param valueMap 存储标识符和对应值的映射关系的Map
     * @return 替换标识符后的结果字符串
     */
    public static String replaceIdentifierValue(String destinationString, Map<String, String> valueMap){
        //正则表达式匹配规则的字符串,这里用来匹配${}字符串
        String regex = "\\$\\{(.+?)\\}";
        //匹配模式  符合regex中正则表达式匹配规则的匹配模式
        Pattern pattern = Pattern.compile(regex);
        //匹配器 对目标字符串destinationString，使用pattern匹配模式得到的匹配器
        Matcher matcher = pattern.matcher(destinationString);
        //缓存字符串
        StringBuffer stringBuffer = new StringBuffer();
        while (matcher.find()){
            // 获取匹配到的标识符 group(0)就是指的整个串，group(1)指的是第一个括号里的东西,以此类推
            String key = matcher.group(1);
            // 根据标识符从valueMap中获取对应的值
            String value = valueMap.get(key);
            if(value == null){
                matcher.appendReplacement(stringBuffer,"");
            }else {
                value.replaceAll("\\$","RDS_CHAR_DOLLAR");
                // 将匹配到的标识符替换为对应的值，并将结果追加到stringBuffer中
                matcher.appendReplacement(stringBuffer,value);
            }
        }
        // 将匹配到的剩余部分追加到stringBuffer中
        matcher.appendTail(stringBuffer);
        return stringBuffer.toString().replaceAll("RDS_CHAR_DOLLAR","\\$");
    }
    
    /**
     * 将字符串转换成驼峰命名法
     * @param str 字符串
     * @return 驼峰命名法
     */
    public static String toCamelCase(String str) {
        if (str == null) {
            return null;
        }
        // 将字符串中的下划线替换成驼峰命名法
        StringBuilder sb = new StringBuilder();
        // 用于标记是否需要将下一个字符转换成大写
        boolean flag = false;
        return getCamelCaseString(str, sb, flag);
    }
    
    /**
     * 将字符串转换成下划线命名法
     * @param str 字符串
     * @return 下划线命名法
     */
    public static String toUnderlineCase(String str) {
        if (str == null) {
            return null;
        }
        // 将字符串中的驼峰命名法替换成下划线命名法
        StringBuilder sb = new StringBuilder();
        for (char c : str.toCharArray()) {
            if (Character.isUpperCase(c)) {
                sb.append('_');
                sb.append(Character.toLowerCase(c));
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }
    
    /**
     * 将字符串转换成首字母大写的驼峰命名法
     * @param str 字符串
     * @return 首字母大写的驼峰命名法
     */
    public static String toCapitalizeCamelCase(String str) {
        if (str == null) {
            return null;
        }
        // 将字符串中的下划线替换成驼峰命名法
        StringBuilder sb = new StringBuilder();
        // 用于标记是否需要将下一个字符转换成大写
        boolean flag = true;
        return getCamelCaseString(str, sb, flag);
    }

    /**
     * 将字符串转换成驼峰命名法
     * @param str 字符串
     * @param sb StringBuilder
     * @param flag 用于标记是否需要将下一个字符转换成大写 true=需要；false=不需要
     * @return 驼峰命名法
     */
    @NotNull
    private static String getCamelCaseString(String str, StringBuilder sb, boolean flag) {
        for (char c : str.toCharArray()) {
            if (c == '_') {
                flag = true;
            } else {
                if (flag) {
                    sb.append(Character.toUpperCase(c));
                    flag = false;
                } else {
                    sb.append(c);
                }
            }
        }
        return sb.toString();
    }

    /**
     * 判断给定字符串是否与正则表达式匹配。
     *
     * @param str 需要进行匹配的原始字符串。
     * @param regex 用于匹配的正则表达式。
     * @return 如果原始字符串与正则表达式匹配，则返回true；否则返回false。
     */
    public static boolean match(String str, String regex) {
        // 使用字符串的matches方法进行正则表达式匹配
        return str.matches(regex);
    }

    /**
     * 验证手机号码是否合法
     * @param mobile 待验证的手机号码
     * @return 如果手机号码合法，返回true；否则返回false。
     */
    public static boolean isMobile(String mobile) {
        // 使用正则表达式进行手机号码的匹配
        return match(mobile, RegexStr.MOBILE);
    }

    /**
     * 验证邮箱地址是否合法
     * @param email 待验证的邮箱地址
     * @return 如果邮箱地址合法，返回true；否则返回false。
     */
    public static boolean isEmail(String email) {
        // 使用正则表达式进行邮箱的匹配
        return match(email, RegexStr.EMAIL);
    }

    /**
     * 验证URL地址是否合法
     * @param url 待验证的URL地址
     * @return 如果URL地址合法，返回true；否则返回false。
     */
    public static boolean isUrl(String url) {
        // 使用正则表达式进行URL的匹配
        return match(url, RegexStr.URL);
    }

    /**
     * 验证QQ号码是否合法
     * @param qq 待验证的QQ号码
     * @return 如果QQ号码合法，返回true；否则返回false。
     */
    public static boolean isQQ(String qq) {
        // 使用正则表达式进行QQ号的匹配
        return match(qq, RegexStr.QQ);
    }

    /**
     * 随机生成6位数验证码
     * @return 生成的6位数验证码
     */
    public static String randomCode() {
        // 生成6位数的随机数
        return String.valueOf((int) ((Math.random() * 9 + 1) * 100000));
    }

    /**
     * 获取随机数 普通短信验证码 性能好，线程安全
     * @return 随机数
     */
    public static String threadLocalRandom(){
        ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
        return String.valueOf(threadLocalRandom.nextInt(100_000, 1_000_000));
    }

    /**
     * 获取随机数 密码学安全随机 性能一般，线程安全
     * 支付/安全验证
     * @return 随机数
     */
    public static String secureRandom() {
        SecureRandom secureRandom = new SecureRandom();
        return String.format("%06d", secureRandom.nextInt(1_000_000));
    }



    /**
     * 从字符串中截取指定分隔符之前的子字符串。
     *
     * @param str          原始字符串
     * @param separator    分隔符
     * @return             截取后的子字符串，如果没有找到分隔符，则返回原始字符串
     */
    public static String substringBefore(String str, String separator) {
        if (isEmpty(str) || separator == null) {
            return str;
        }
        if (separator.isEmpty()) {
            return "";
        }
        int pos = str.indexOf(separator);
        // 如果没有找到分隔符，则返回原始字符串
        if (pos == -1) {
            return str;
        }
        return str.substring(0, pos);
    }

    /**
     * 从字符串中截取指定分隔符之后的子字符串。
     *
     * @param str          原始字符串
     * @param separator    分隔符
     * @return             截取后的子字符串，如果没有找到分隔符，则返回原始字符串
     */
    public static String substringAfter(String str, String separator){
        if (isEmpty(str) || separator == null) {
            return str;
        }
        if (separator.isEmpty()) {
            return "";
        }
        int pos = str.indexOf(separator);
        if (pos == -1) {
            return str;
        }
        return str.substring(pos);
    }
}
